﻿using System;
using System.Collections.Generic;
using System.Threading;

using FLEN_Sim.funct;
using FLEN_Sim.gui.map;
using FLEN_Sim.config;

using GMap.NET.RouteProviders;

namespace FLEN_Sim.containerobjects
{
    public class AmbulanceVehicle : IFLENObject
    {
        private int city, type, id, status;
        private AmbulanceStation station;
        private string callsign, crewChange;
        private bool inStation;
        private Emergency emergency;
        private GMap.NET.WindowsForms.GMapRoute route;
        private GMap.NET.PointLatLng currentLocation;
        private Thread moveMarkerThread;
        private MapMarkers.AmbulanceVehicleMarker marker;

        public double traveledDistInM = 0.0;
        public DateTime timeStart, timeEnd;

        

        public AmbulanceVehicle(AmbulanceStation Station, int Type, int ID, string CrewChange)
        {
            station = Station;
            station.addVehicle(this);
            this.Status = 2;
            inStation = true;
            type = Type;
            id = ID;
            crewChange = CrewChange;
            city = CityNumbers.getCityByName(station.City);
            callsign = city.ToString() + "-" + type.ToString() + "-" + id.ToString();
            emergency = null;
            route = null;
            currentLocation = station.Marker.Position;
            moveMarkerThread = null;
        }

        public int City
        {
            get
            {
                return city;
            }
        }

        public int Type
        {
            get
            {
                return type;
            }
        }

        public int ID
        {
            get
            {
                return id;
            }
        }

        public int Status
        {
            get
            {
                return status;
            }
            set
            {
                if (value >= 0 && value < 10)
                {
                    status = value;
                    this.station.checkState();

                    if (value == 4)
                    {
                        Config.textbox.Text = "Strecke: " + this.traveledDistInM + "m\n" +                            
                        "Zeit: "+this.timeEnd.Subtract(this.timeStart).Minutes+" min";
                    }
                }
                else
                    throw new ArgumentException("Status must be between 0 and 9!");
            }
        }

        public string Callsign
        {
            get
            {
                return callsign;
            }
        }

        public AmbulanceStation Station
        {
            get
            {
                return station;
            }
        }

        public bool InStation
        {
            get
            {
                return inStation;
            }
            set
            {
                inStation = value;
            }
        }

        public void assignToEmergency(Emergency em)
        {
            if (emergency == null)
            {
                emergency = em;
                em.assignVehicle(this);

                this.Status = 3;

                YouRouteProvider routeProv = new YouRouteProvider();
                string url = routeProv.MakeUrl(currentLocation, em.Marker.Position, "");
                List<GMap.NET.PointLatLng> pointList = routeProv.GetRoutePoints(url);

                route = new GMap.NET.WindowsForms.GMapRoute(pointList, em.Address);

                if (Config.DEBUG_SHOWROUTES)
                {
                    foreach (GMap.NET.WindowsForms.GMapOverlay ov in Config.CONTROL.Overlays)
                    {
                        if (ov.Id.Equals("Routen"))
                        {
                            ov.Routes.Add(route);
                            Config.CONTROL.Refresh();
                            break;
                        }
                    }
                }

                if (moveMarkerThread != null)
                {
                    moveMarkerThread.Abort();
                    moveMarkerThread = null;
                }

                timeStart = DateTime.Now;
                moveMarkerThread = new Thread(new ThreadStart(moveMarker));
                moveMarkerThread.Start();
                Config.THREAD_LIST.Add(moveMarkerThread);
            }
            else
            {
                /*
                 * 
                 * anderen Einsatz zuweisen, nur möglich in Status 3
                 * 
                 */
            }
        }

        private delegate void RefreshControl();

        private void moveMarker()
        {
            if (marker == null)
            {
                marker = new MapMarkers.AmbulanceVehicleMarker(this.currentLocation);
            }
            
            foreach (GMap.NET.WindowsForms.GMapOverlay ov in Config.CONTROL.Overlays)
            {
                if (ov.Id.Equals("Fahrzeuge"))
                {
                    ov.Markers.Add(this.marker);
                    if (Config.CONTROL.InvokeRequired)
                    {
                        RefreshControl rc = new RefreshControl(Config.CONTROL.Refresh);
                        Config.CONTROL.Invoke(rc);
                    }
                    else
                    {
                        Config.CONTROL.Refresh();
                    }
                    break;
                }
            }

            GMap.NET.PointLatLng currentPoint = this.currentLocation;
            GMap.NET.PointLatLng nextPoint = route.Points[0];
            route.Points.RemoveAt(0);

            double faktor = 0.0005;


            double speed = Config.SPEED70;
            double speedPerMS = speed / 100;
            speed = speedPerMS;

            //ÜBER REELEN SPEED GEHEN!

            double difLat = (currentPoint.Lat > nextPoint.Lat ? (currentPoint.Lat - nextPoint.Lat) : (nextPoint.Lat - currentPoint.Lat));
            double difLon = (currentPoint.Lng > nextPoint.Lng ? (currentPoint.Lng - nextPoint.Lng) : (nextPoint.Lng - currentPoint.Lng));

            double nextDifM = 0.0;
            double difM = this.getDistanceInM(currentPoint, nextPoint) + nextDifM;
            double speedFactor = (this.getPercent(speed, difM) / 100);
            double newLat = 0.0, newLon = 0.0;

            while (status == 3)
            {
                Thread.Sleep(10);

                if (currentPoint == nextPoint)
                {
                    if (route.Points.Count > 1)
                    {
                        nextPoint = route.Points[0];
                        route.Points.RemoveAt(0);
                    }
                    else
                    {
                        this.Status = 4;
                        // ROUTE LÖSCHEN
                    }
                }

                difLat = (currentPoint.Lat > nextPoint.Lat ? (currentPoint.Lat - nextPoint.Lat) : (nextPoint.Lat - currentPoint.Lat));
                difLon = (currentPoint.Lng > nextPoint.Lng ? (currentPoint.Lng - nextPoint.Lng) : (nextPoint.Lng - currentPoint.Lng));
                difM = this.getDistanceInM(currentPoint, nextPoint) + nextDifM;
                speedFactor = (this.getPercent(speed, difM) / 100);

                if (difM > speed)
                {
                    newLat = (currentPoint.Lat > nextPoint.Lat ? currentPoint.Lat - (speedFactor * difLat) : currentPoint.Lat + (speedFactor * difLat));
                    newLon = (currentPoint.Lng > nextPoint.Lng ? currentPoint.Lng - (speedFactor * difLon) : currentPoint.Lng + (speedFactor * difLon));

                    nextDifM = 0.0;

                    this.traveledDistInM += speed;

                    currentPoint = new GMap.NET.PointLatLng(newLat, newLon);
                }
                else
                {
                    currentPoint = nextPoint;
                    nextDifM = speed - difM; //Übertrag

                    this.traveledDistInM += difM;
                }

                marker.Position = currentPoint;

                if (Config.CONTROL.InvokeRequired)
                {
                    RefreshControl rc = new RefreshControl(Config.CONTROL.Refresh);
                    Config.CONTROL.Invoke(rc);
                }
                else
                {
                    Config.CONTROL.Refresh();
                }

                timeEnd = DateTime.Now;
            }

            /*
             * neuer Plan:
             * 1. Lat/Lon-Differenz zwischen currentPoint&nextPoint ausrechnen
             * 2. Differenz in m zwischen currentPoint&nextPoint ausrechnen
             * 3. Wenn ...
             *      Differenz in m > mit Speed zurückgelegte Strecke
             *    Dann ...
             *      um Differenz vorziehen* und currentPoint = currentPoint-+Lat/Lon-Differenz
             *    Sonst ...
             *      bis nextPoint vorziehen und die übriggebliebene Differenz auf neue Differenz addieren
             *      (einfach die m addieren!)
             * 
             * 
             *      *: Differenz prozentmäßig zurückrechnen, so dass wir x% der Strecke vorwärts gegangen sind
             *          diese Prozente dann auf Lat/Lon rechnen und currentPoint setzen
             *          
             * 
             * 
             * 
             *      IRGENDWANN das ganze dann mal auf m/ms umrechnen statt m/s ;-)
             * 
             */

            //while (this.status == 3)
            //{
            //    Thread.Sleep(1000); //1s warten lassen, da wir in m/s laufen =)

            //    if (currentPoint.Lat == nextPoint.Lat && currentPoint.Lng == nextPoint.Lng)
            //    {
            //        currentPoint = nextPoint;
            //        if (route.Points.Count > 1)
            //        {
            //            nextPoint = route.Points[0];
            //            route.Points.RemoveAt(0);
            //        }
            //        else
            //        {
            //            this.Status = 4;
            //            // ROUTE LÖSCHEN
            //        }
            //    }
            //    if (currentPoint.Lat == nextPoint.Lat)
            //    {
            //        //sonderfall, nur in lon richtung laufen
            //        difLon = (currentPoint.Lng > nextPoint.Lng ? ((currentPoint.Lng - nextPoint.Lng)) : (nextPoint.Lng - currentPoint.Lng)) * faktor;
            //        if (currentPoint.Lng < nextPoint.Lng)
            //        {
            //            currentPoint = new GMap.NET.PointLatLng(currentPoint.Lat, currentPoint.Lng + difLon);
            //        }
            //        else if (currentPoint.Lng > nextPoint.Lng)
            //        {
            //            currentPoint = new GMap.NET.PointLatLng(currentPoint.Lat, currentPoint.Lng - difLon);
            //        }
            //    }
            //    else if (currentPoint.Lng == nextPoint.Lng)
            //    {
            //        //sonderfall, nur in lat richtung laufen
            //        difLat = (currentPoint.Lat > nextPoint.Lat ? (currentPoint.Lat - nextPoint.Lat) : (nextPoint.Lat - currentPoint.Lat)) * faktor;

            //        if (currentPoint.Lat > nextPoint.Lat)
            //        {
            //            currentPoint = new GMap.NET.PointLatLng(currentPoint.Lat - difLat, currentPoint.Lng);
            //        }
            //        else if (currentPoint.Lat < nextPoint.Lat)
            //        {
            //            currentPoint = new GMap.NET.PointLatLng(currentPoint.Lat + difLat, currentPoint.Lng);
            //        }
            //    }
            //    else
            //    {
            //        difLat = (currentPoint.Lat > nextPoint.Lat ? (currentPoint.Lat - nextPoint.Lat) : (nextPoint.Lat - currentPoint.Lat)) * faktor;
            //        difLon = (currentPoint.Lng > nextPoint.Lng ? (currentPoint.Lng - nextPoint.Lng) : (nextPoint.Lng - currentPoint.Lng)) * faktor;

            //        if(currentPoint.Lat > nextPoint.Lat && currentPoint.Lng < nextPoint.Lng)
            //        {
            //            currentPoint = new GMap.NET.PointLatLng(currentPoint.Lat - difLat, currentPoint.Lng + difLon);                        
            //        }
            //        else if (currentPoint.Lat < nextPoint.Lat && currentPoint.Lng > nextPoint.Lng)
            //        {
            //            currentPoint = new GMap.NET.PointLatLng(currentPoint.Lat + difLat, currentPoint.Lng - difLon);
            //        }
            //        else if (currentPoint.Lat < nextPoint.Lat && currentPoint.Lng < nextPoint.Lng)
            //        {
            //            currentPoint = new GMap.NET.PointLatLng(currentPoint.Lat + difLat, currentPoint.Lng + difLon);
            //        }
            //        else if (currentPoint.Lat > nextPoint.Lat && currentPoint.Lng > nextPoint.Lng)
            //        {
            //            currentPoint = new GMap.NET.PointLatLng(currentPoint.Lat - difLat, currentPoint.Lng - difLon);
            //        }
            //    }

            //    marker.Position = currentPoint;

            //    double distance = this.getDistanceInM(currentPoint, nextPoint);

            //    if (difLat < 0.000001 || difLon < 0.000001)
            //    {
            //        currentPoint = nextPoint;
            //    }

            //    if (Config.CONTROL.InvokeRequired)
            //    {
            //        RefreshControl rc = new RefreshControl(Config.CONTROL.Refresh);
            //        Config.CONTROL.Invoke(rc);
            //    }
            //    else
            //    {
            //        Config.CONTROL.Refresh();
            //    }
            //}
        }

        public void deassignFromEmergency()
        {
            emergency.deassignVehicle(this);
            emergency = null;
        }

        private double getDistanceInM(double lat1, double lat2, double lon1, double lon2)
        {
            //            distance = sqrt(dx * dx + dy * dy)

            //            mit distance: Entfernung in km 
            //            dx = 111.3 * cos(lat) * (lon1 - lon2)
            //            lat = (lat1 + lat2) / 2 * 0.01745
            //            dy = 111.3 * (lat1 - lat2)
            //            lat1, lat2, lon1, lon2: Breite, Länge in Grad

            double dy = 111.3 * (lat1 - lat2);
            double dx = 111.3 * Math.Cos((lat1 + lat2) / 2 * 0.01745) * (lon1 - lon2);

            return Math.Sqrt(dx*dx+dy*dy)*1000;
            
        }

        private double getDistanceInM(GMap.NET.PointLatLng p1, GMap.NET.PointLatLng p2)
        {
            return getDistanceInM(p1.Lat, p2.Lat, p1.Lng, p2.Lng);
        }

        private double getPercent(double thatMuch, double From)
        {
            return (100/From)*thatMuch;
        }
    }
}
